//
#include<stdlib.h>
#include<stdio.h>
#include <string.h>
#include "global.h"




/* table to tell if char can be part of a C identifier. */
char is_idchar[256];
/* table to tell if char can be first char of a c identifier. */
char is_idstart[256];
/* table to tell if c is horizontal space.  isspace() thinks that
newline is space; this is not a good idea for this program. */
char is_hor_space[256];
char is_space[256];

char *progname;

/* Current maximum length of directory names in the search path
for include files.  (Altered as we get more of them.)  */

int max_include_len = sizeof("/usr/local/lib/gcc-include");

/* Nonzero means copy comments into the output file.  */

int put_out_comments = 0;

/* Nonzero means don't process the ANSI trigraph sequences.  */

int no_trigraphs = 0;

/* Nonzero means print the names of included files rather than
the preprocessed output.  1 means just the #include "...",
2 means #include <...> as well.  */

int print_deps = 0;

/* Nonzero means don't output line number information.  */

int no_line_commands;

/* Nonzero means inhibit output of the preprocessed text
and instead output the definitions of all user-defined macros
in a form suitable for use as input to cccp.  */

int dump_macros;

/* Nonzero means give all the error messages the ANSI standard requires.  */

int pedantic;

/* Nonzero means warn if slash-star appears in a comment.  */

int warn_comments;

/* Nonzero means try to imitate old fashioned non-ANSI preprocessor.  */

//int traditional;

/* Nonzero causes output not to be done,
but directives such as #define that have side effects
are still obeyed.  */

int no_output;

#define INPUT_STACK_MAX 200
struct file_buf {
	char *fname;
	int lineno;
	int length;
	U_CHAR *buf;
	U_CHAR *bufp;
	/* Macro that this level is the expansion of.
	Included so that we can reenable the macro
	at the end of this level.  */
	struct hashnode *macro;
	/* Value of if_stack at start of this file.
	Used to prohibit unmatched #endif (etc) in an include file.  */
	struct if_stack *if_stack;
	/* Object to be freed at end of input at this level.  */
	U_CHAR *free;
} instack[INPUT_STACK_MAX];

/* Current nesting level of input sources.
`instack[indepth]' is the level currently being read.  */
int indepth = -1;

typedef struct file_buf FILE_BUF;

#define HASHSIZE 1403
HASHNODE *hashtab[HASHSIZE];
#define SKIP_WHITE_SPACE(p) do { while (is_hor_space[*p]) p++; } while (0)
#define SKIP_ALL_WHITE_SPACE(p) do { while (is_space[*p]) p++; }  while (0)


HASHNODE *
	install (U_CHAR *name,int len, enum node_type  type,int value,int hash)
	/* watch out here if sizeof (U_CHAR *) != sizeof (int) */
{
	register HASHNODE *hp;
	register int i, bucket;
	register U_CHAR *p, *q;

	if (len < 0) {
		p = name;
		while (is_idchar[*p])
			p++;
		len = p - name;
	}

	if (hash < 0)
		hash = hashf (name, len, HASHSIZE);

	i = sizeof (HASHNODE) + len + 1;
	hp = (HASHNODE *) malloc (i);
	bucket = hash;
	hp->bucket_hdr = &hashtab[bucket];
	hp->next = hashtab[bucket];
	hashtab[bucket] = hp;
	hp->prev = NULL;
	if (hp->next != NULL)
		hp->next->prev = hp;
	hp->type = type;
	hp->length = len;
	hp->value.ival = value;
	hp->name = ((U_CHAR *) hp) + sizeof (HASHNODE);
	p = hp->name;
	q = name;
	for (i = 0; i < len; i++)
		*p++ = *q++;
	hp->name[len] = 0;
	return hp;
}

void initialize_random_junk()
{
	register int i;

	/*
	* Set up is_idchar and is_idstart tables.  These should be
	* faster than saying (is_alpha (c) || c == '_'), etc.
	* Must do set up these things before calling any routines tthat
	* refer to them.
	*/
	for (i = 'a'; i <= 'z'; i++) {
		++is_idchar[i - 'a' + 'A'];
		++is_idchar[i];
		++is_idstart[i - 'a' + 'A'];
		++is_idstart[i];
	}
	for (i = '0'; i <= '9'; i++)
		++is_idchar[i];
	++is_idchar['_'];
	++is_idstart['_'];
	++is_idchar['$'];
	++is_idstart['$'];

	/* horizontal space table */
	++is_hor_space[' '];
	++is_hor_space['\t'];
	++is_hor_space['\v'];
	++is_hor_space['\f'];

	++is_space[' '];
	++is_space['\t'];
	++is_space['\v'];
	++is_space['\f'];
	++is_space['\n'];

	install ((U_CHAR *)"__LINE__", -1, T_SPECLINE, 0, -1);
	install ((U_CHAR *)"__DATE__", -1, T_DATE, 0, -1);
	install ((U_CHAR *)"__FILE__", -1, T_FILE, 0, -1);
	install ((U_CHAR *)"__VERSION__", -1, T_VERSION, 0, -1);
	install ((U_CHAR *)"__TIME__", -1, T_TIME, 0, -1);
	install ((U_CHAR *)"__STDC__", -1, T_CONST, 1, -1);
	/*  install ("__GNU__", -1, T_CONST, 1, -1);  */
	/*  This is supplied using a -D by the compiler driver
	so that it is present only when truly compiling with GNU C.  */
}

/* Return 1 if two parts of two macro definitions are effectively different.
One of the parts starts at BEG1 and has LEN1 chars;
the other has LEN2 chars at BEG2.
Any sequence of whitespace matches any other sequence of whitespace.
FIRST means these parts are the first of a macro definition;
so ignore leading whitespace entirely.
LAST means these parts are the last of a macro definition;
so ignore trailing whitespace entirely.  */
int
	comp_def_part (int first,U_CHAR * beg1,int len1, U_CHAR *beg2,int len2,int last)
	/*     int first;
	U_CHAR *beg1, *beg2;
	int len1, len2;
	int last;*/
{
	register U_CHAR *end1 = beg1 + len1;
	register U_CHAR *end2 = beg2 + len2;
	if (first) {
		while (beg1 != end1 && is_space[*beg1]) beg1++;
		while (beg2 != end2 && is_space[*beg2]) beg2++;
	}
	if (last) {
		while (beg1 != end1 && is_space[end1[-1]]) end1--;
		while (beg2 != end2 && is_space[end2[-1]]) end2--;
	}
	while (beg1 != end1 && beg2 != end2) {
		if (is_space[*beg1] && is_space[*beg2]) {
			while (beg1 != end1 && is_space[*beg1]) beg1++;
			while (beg2 != end2 && is_space[*beg2]) beg2++;
		} else if (*beg1 == *beg2) {
			beg1++; beg2++;
		} else break;
	}
	return (beg1 != end1) || (beg2 != end2);
}

/*
* return zero if two DEFINITIONs are isomorphic
*/
static int
	compare_defs (DEFINITION *d1, DEFINITION *d2)
	//DEFINITION *d1, *d2;
{
	register struct reflist *a1, *a2;
	register U_CHAR *p1 = d1->expansion;
	register U_CHAR *p2 = d2->expansion;
	int first = 1;

	if (d1->nargs != d2->nargs)
		return 1;
	if (strcmp ((char*)d1->argnames, (char*)d2->argnames))
		return 1;
	for (a1 = d1->pattern, a2 = d2->pattern; a1 && a2;
		a1 = a1->next, a2 = a2->next) {
			if (!((a1->nchars == a2->nchars && ! strncmp ((char*)p1, (char*)p2, a1->nchars))
				|| ! comp_def_part (first, p1, a1->nchars, p2, a2->nchars, 0))
				|| a1->argno != a2->argno
				|| a1->stringify != a2->stringify
				|| a1->raw_before != a2->raw_before
				|| a1->raw_after != a2->raw_after)
				return 1;
			first = 0;
			p1 += a1->nchars;
			p2 += a2->nchars;
	}
	if (a1 != a2)
		return 1;
	if (comp_def_part (first, p1, d1->length - (p1 - d1->expansion),
		p2, d2->length - (p2 - d2->expansion), 1))
		return 1;
	return 0;
}

/*
* find the most recent hash node for name name (ending with first
* non-identifier char) installed by install
*
* If LEN is >= 0, it is the length of the name.
* Otherwise, compute the length by scanning the entire name.
*
* If HASH is >= 0, it is the precomputed hash code.
* Otherwise, compute the hash code.
*/
HASHNODE *
	lookup (U_CHAR *name, int len, int hash)
	/*U_CHAR *name;
	int len;
	int hash;*/
{
	register U_CHAR *bp;
	register HASHNODE *bucket;

	if (len < 0) {
		for (bp = name; is_idchar[*bp]; bp++) ;
		len = bp - name;
	}

	if (hash < 0)
		hash = hashf (name, len, HASHSIZE);

	bucket = hashtab[hash];
	while (bucket) {
		if (bucket->length == len && strncmp ((char*)bucket->name, (char*)name, len) == 0)
			return bucket;
		bucket = bucket->next;
	}
	return NULL;
}

/* Structure allocated for every #define.  For a simple replacement
such as
#define foo bar ,
nargs = -1, the `pattern' list is null, and the expansion is just
the replacement text.  Nargs = 0 means a functionlike macro with no args,
e.g.,
#define getchar() getc (stdin) .
When there are args, the expansion is the replacement text with the
args squashed out, and the reflist is a list describing how to
build the output from the input: e.g., "3 chars, then the 1st arg,
then 9 chars, then the 3rd arg, then 0 chars, then the 2nd arg".
The chars here come from the expansion.  Whatever is left of the
expansion after the last arg-occurrence is copied after that arg.
Note that the reflist can be arbitrarily long---
its length depends on the number of times the arguments appear in
the replacement text, not how many args there are.  Example:
#define f(x) x+x+x+x+x+x+x would have replacement text "++++++" and
pattern list
{ (0, 1), (1, 1), (1, 1), ..., (1, 1), NULL }
where (x, y) means (nchars, argno). */

/*
x
0 chars 1 argsno

x +           x
^           ^
|           |  
1 char     1 argsno
1 char 1 argsno

*/




int do_define (U_CHAR *buf, U_CHAR* limit,FILE_BUF* op,struct directive * keyword)
{
	unsigned char *symname;
	int sym_length;
	int dollar_seen=0;
	U_CHAR *bp;
	bp=buf;
	limit=bp+30;

	DEFINITION *defn;
	int arglengths = 0;		/* Accumulate lengths of arg names
							plus number of args.  */
	int hashcode;

	while (is_hor_space[*bp])
		bp++;
	if (!is_idstart[*bp])
		printf ("macro name starts with a digit");

	symname = bp; //remeber where it started.

	while (is_idchar[*bp] && bp < limit) {
		if (*bp == '$') {
			if (! dollar_seen)
				printf ("ANSI C forbids `$' in identifier (first use here)");
			dollar_seen = 1;
		}
		bp++;
	}
	sym_length = bp - symname;

	/* lossage will occur if identifiers or control keywords are broken
	across lines using backslash.  This is not the right place to take
	care of that. */

	if (*bp == '(') {
		struct arglist *arg_ptrs = NULL;
		int argno = 0;

		bp++;			/* skip '(' */
		SKIP_WHITE_SPACE (bp);

		/* Loop over macro argument names.  */
		while (*bp != ')') {
			struct arglist *temp;

			temp = (struct arglist *) malloc(sizeof (struct arglist));
			temp->name = bp;
			temp->next = arg_ptrs;
			temp->argno = argno++;
			arg_ptrs = temp;

			if (!is_idstart[*bp])
				warning ("parameter name starts with a digit in #define");

			/* Find the end of the arg name.  */
			while (is_idchar[*bp]) {
				if (*bp == '$' && 1) {
					if (! dollar_seen)
						warning ("ANSI C forbids `$' in identifier (first use here)");
					dollar_seen = 1;
				}
				bp++;
			}
			temp->length = bp - temp->name;
			arglengths += temp->length + 2;
			SKIP_WHITE_SPACE (bp);
			if (temp->length == 0 || (*bp != ',' && *bp != ')')) {
				error ("badly punctuated parameter list in #define");
				goto nope;
			}
			if (*bp == ',') {
				bp++;
				SKIP_WHITE_SPACE (bp);
			}
			if (bp >= limit) {
				error ("unterminated parameter list in #define");
				goto nope;
			}
		}

		++bp;			/* skip paren */
		/* Skip exactly one space or tab if any.  */
		if (bp < limit && (*bp == ' ' || *bp == '\t')) ++bp;
		/* now everything from bp before limit is the definition. */
		defn = collect_expansion (bp, limit, argno, arg_ptrs);

		/* Now set defn->argnames to the result of concatenating
		the argument names in reverse order
		with comma-space between them.  */
		defn->argnames = (U_CHAR *) malloc (arglengths + 1);
		{
			struct arglist *temp;
			int i = 0;
			for (temp = arg_ptrs; temp; temp = temp->next)
			{
				bcopy (temp->name, &defn->argnames[i], temp->length);
				i += temp->length;
				if (temp->next != 0) {
					defn->argnames[i++] = ',';
					defn->argnames[i++] = ' ';
				}
			}
			defn->argnames[i] = 0;
		}
	} else {
		/* simple expansion or empty definition; gobble it */
		if (is_hor_space[*bp])
			++bp;		/* skip exactly one blank/tab char */
		/* now everything from bp before limit is the definition. */
		defn = collect_expansion (bp, limit, -1, 0);
		defn->argnames = (U_CHAR *) "";
	}

	hashcode = hashf (symname, sym_length, HASHSIZE);

	{
		HASHNODE *hp;
		if ((hp = lookup (symname, sym_length, hashcode)) != NULL) {
			if (hp->type != T_MACRO
				|| compare_defs (defn, hp->value.defn)) {
					U_CHAR *msg;			/* what pain... */
					msg = (U_CHAR *) malloc (sym_length + 20);
					bcopy (symname, msg, sym_length);
					strcpy ((char*)msg + sym_length, " redefined");
					warning ((char*)msg);
			}
			/* Replace the old definition.  */
			hp->type = T_MACRO;
			hp->value.defn = defn;
		} else
		{
		}
		//install (symname, sym_length, T_MACRO, defn, hashcode);
	}

	return 0;

nope:
	return 1;

}


/*
How HashTable work.

hp->bucket_hdr-----------------+   
hp->next--------------------+  |   
|  |
|  |
|  |
|  |
+-----+               V  |
|     |---->hp<------>P  |
|     |<---+          |  |
+-----+    |          |  |
|     |    |          |  |
+-----+    |          |  |
|          |  |
|          |  |
+-------+        |  |
|pointer|<-------+  |
+-------+<----------+

*/

char *deps_buffer;

/* Number of bytes allocated in above.  */
int deps_allocated_size;

/* Number of bytes used.  */
int deps_size;

/* Number of bytes since the last newline.  */
int deps_column;

/* Nonzero means -I- has been seen,
so don't look for #include "foo" the source-file directory.  */
int ignore_srcdir;

char *version_string = "1.22";

struct directory_stack
{
	struct directory_stack *next;
	char *fname;
};

struct directory_stack *include = 0;	/* First dir to search */
/* First dir to search for <file> */
struct directory_stack *first_bracket_include = 0;
struct directory_stack *last_include = 0;	/* Last in chain */

struct directive {
	int length;			/* Length of name */
	int (*func)(U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword);		/* Function to handle directive */
	char *name;			/* Name of directive */
	enum node_type type;		/* Code which describes which directive. */
	char angle_brackets;		/* Nonzero => don't delete comments.  */
};




struct if_stack {
  struct if_stack *next;	/* for chaining to the next stack frame */
  char *fname;		/* copied from input when frame is made */
  int lineno;			/* similarly */
  int if_succeeded;		/* true if a leg of this if-group
				    has been passed through rescan */
  enum node_type type;		/* type of last directive seen in this group */
};

struct if_stack *if_stack;



int do_if (U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword)
{
	return 0;
}

int do_xifdef (U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword)
{
	return 0;
}

int do_endif (U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword)
{
	return 0;
}

int do_else (U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword)
{
	return 0;
}

int do_elif (U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword)
{
	return 0;
}

int do_line (U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword)
{
	return 0;
}

int do_include (U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword)
{
	return 0;
}

int do_undef (U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword)
{
	return 0;
}

int do_error (U_CHAR *buf, U_CHAR *limit, FILE_BUF *op, struct directive *keyword)
{
	return 0;
}

struct directive directive_table[] = {
	{  6, do_define, "define", T_DEFINE},
	{  2, do_if, "if", T_IF},
	{  5, do_xifdef, "ifdef", T_IFDEF},
	{  6, do_xifdef, "ifndef", T_IFNDEF},
	{  5, do_endif, "endif", T_ENDIF},
	{  4, do_else, "else", T_ELSE},
	{  4, do_elif, "elif", T_ELIF},
	{  4, do_line, "line", T_LINE},
	{  7, do_include, "include", T_INCLUDE, 1},
	{  5, do_undef, "undef", T_UNDEF},
	{  5, do_error, "error", T_ERROR},
	//#ifdef SCCS_DIRECTIVE
#if 0
	{  4, do_sccs, "sccs", T_SCCS},
#endif
#if 0
	{  6, do_pragma, "pragma", T_PRAGMA},
#endif
	{  -1, 0, "", T_UNUSED},
};

void make_definition (U_CHAR *str)
{
  FILE_BUF *ip;
  struct directive *kt;
  U_CHAR *buf, *p;

  buf = str;
  p = str;
  while (is_idchar[*p]) p++;
  if (*p == 0) {
    buf = (U_CHAR *) malloc(p - buf + 4);
    strcpy ((char *)buf, (char*)str);
    strcat ((char *)buf, " 1");
  }
  
  ip = &instack[++indepth];
  ip->fname = "*Initialization*";

  ip->buf = ip->bufp = buf;
  ip->length = strlen ((char*)buf);
  ip->lineno = 1;
  ip->macro = 0;
  ip->free = 0;
  ip->if_stack = if_stack;

  for (kt = directive_table; kt->type != T_DEFINE; kt++)
    ;

  /* pass NULL as output ptr to do_define since we KNOW it never
     does any output.... */
  do_define ((U_CHAR*)buf, (U_CHAR*)buf + strlen ((char*)buf) , NULL, kt);
  --indepth;
}

int main(int argc, char ** argv) 
{ 
	//char *line = "#define fun(x) 2*x";
	//const char *name = "__LINE__";
	//unsigned char *p;
	//p = (unsigned char *)name;

	//int a[1] = {1};
	//printf("%d\n",0[a]);

	//initialize_random_junk();
	//install (p, -1, T_SPECLINE, 0, -1);

	int st_mode;
	long st_size;
	char *in_fname, *out_fname;
	int f, i;
	FILE_BUF *fp;
	char **pend_files = (char **) xmalloc (argc * sizeof (char *));
	char **pend_defs = (char **) xmalloc (argc * sizeof (char *));
	char **pend_undefs = (char **) xmalloc (argc * sizeof (char *));
	int inhibit_predefs = 0;
	int no_standard_includes = 0;

	progname = argv[0];
	in_fname = NULL;
	out_fname = NULL;
	initialize_random_junk ();

	no_line_commands = 0;
	no_trigraphs = 1;
	dump_macros = 0;
	no_output = 0;

	bzero (*pend_files, argc * sizeof (char *));
	bzero (*pend_defs, argc * sizeof (char *));
	bzero (*pend_undefs, argc * sizeof (char *));

	/* Process switches and find input file name.  */

	for (i = 1; i < argc; i++) {
		if (argv[i][0] != '-') 
		{
			if (out_fname != NULL)
				printf ("Usage: %s [switches] input output\n", argv[0]);
			else if (in_fname != NULL) 
			{
				out_fname = argv[i];
				if (! freopen (out_fname, "w", stdout))
				{
				}
				//pfatal_with_name (out_fname);
			}
			else
				in_fname = argv[i];
		} else {
			switch (argv[i][1]) {
			case 'i':
				if (argv[i][2] != 0)
					pend_files[i] = argv[i] + 2;
				else
					pend_files[i] = argv[i+1], i++;
				break;
			case 'o':
				if (out_fname != NULL)
					printf ("Output filename specified twice\n");
				out_fname = argv[++i];
				if (! freopen (out_fname, "w", stdout))
					printf (out_fname);
				break;

			case 'p':
				pedantic = 1;
				break;

			case 't':
				//traditional = 1;
				break;

			case 'W':
				warn_comments = 1;
				break;

			case 'M':
				if (!strcmp (argv[i], "-M"))
					print_deps = 2;
				else if (!strcmp (argv[i], "-MM"))
					print_deps = 1;
				deps_allocated_size = 200;
				deps_buffer = (char *) xmalloc (deps_allocated_size);
				deps_buffer[0] = 0;
				deps_size = 0;
				deps_column = 0;

				break;

			case 'd':
				dump_macros = 1;
				no_output = 1;
				break;

			case 'v':
				{
					fprintf (stderr, "GNU CPP version %s\n", version_string);
				}
				break;

			case 'D':
				{
					char *p, *p1;

					if (argv[i][2] != 0)
						p = argv[i] + 2;
					else
						p = argv[++i];

					if ((p1 = (char *) index (p, '=')) != NULL)
						*p1 = ' ';
					pend_defs[i] = p;
				}
				break;

			case 'U':		/* JF #undef something */
				if (argv[i][2] != 0)
					pend_undefs[i] = argv[i] + 2;
				else
					pend_undefs[i] = argv[i+1], i++;
				break;

			case 'C':
				put_out_comments = 1;
				break;

			case 'E':			/* -E comes from cc -E; ignore it.  */
				break;

			case 'P':
				no_line_commands = 1;
				break;

			case 'T':			/* Enable ANSI trigraphs */
				no_trigraphs = 0;
				break;

			case 'I':			/* Add directory to path for includes.  */
				{
					struct directory_stack *dirtmp;

					if (! ignore_srcdir && !strcmp (argv[i] + 2, "-"))
						ignore_srcdir;
					else {
						dirtmp = (struct directory_stack *)
							malloc (sizeof (struct directory_stack));
						dirtmp->next = 0;		/* New one goes on the end */
						if (include == 0)
							include = dirtmp;
						else
							last_include->next = dirtmp;
						last_include = dirtmp;	/* Tail follows the last one */
						if (argv[i][2] != 0)
							dirtmp->fname = argv[i] + 2;
						else
							dirtmp->fname = argv[++i];
						if (strlen (dirtmp->fname) > max_include_len)
							max_include_len = strlen (dirtmp->fname);
						if (ignore_srcdir && first_bracket_include == 0)
							first_bracket_include = dirtmp;
					}
				}
				break;

			case 'n':
				/* -nostdinc causes no default include directories.
				You must specify all include-file directories with -I.  */
				no_standard_includes = 1;
				break;

			case 'u':
				/* Sun compiler passes undocumented switch "-undef".
				Let's assume it means to inhibit the predefined symbols.  */
				inhibit_predefs = 1;
				break;

			case '\0': /* JF handle '-' as file name meaning stdin or stdout */
				if (in_fname == NULL) {
					in_fname = "";
					break;
				} else if (out_fname == NULL) {
					out_fname = "stdout";
					break;
				}	/* else fall through into error */

			default:
				printf ("Invalid option `%s'\n", argv[i]);
			}
		}
	}


	if (!inhibit_predefs) {
		U_CHAR *p = (U_CHAR *) malloc(strlen ("") + 1);
		strcpy ((char*)p, "");
		while (*p) {
			U_CHAR *q;
			if (p[0] != '-' || p[1] != 'D')
				abort ();
			q = &p[2];
			while (*p && *p != ' ') p++;
			if (*p != 0)
				*p++= 0;
			make_definition (q);
		}
	}

	/* Do defines specified with -D.  */
	for (i = 1; i < argc; i++)
		if (pend_defs[i])
			make_definition ((U_CHAR*)pend_defs[i]);


	return 0; 
} 
